package org.xinghai.sql.main.utils;

import org.xinghai.sql.main.generator.domain.DatabaseTables;
import org.xinghai.sql.main.generator.domain.TableColumns;


import java.util.*;
import java.util.stream.Collectors;

/**
 * 基于外键列自动推断表关系的 SQL 生成工具
 * 无需依赖 TableRelationships，仅通过 DatabaseTables 和 TableColumns 生成完整 SQL
 */
public class EntityParserToSqlUtil {

    // Java 类型到 SQL 类型的反向映射（与 TypeMapperUtils 保持一致）
    private static final Map<Class<?>, String> JAVA_TYPE_TO_SQL = new HashMap<>();
    // 默认级联策略（可根据需求调整）
    private static final String DEFAULT_CASCADE_STRATEGY = "ON DELETE RESTRICT";

    static {
        JAVA_TYPE_TO_SQL.put(Integer.class, "INT");
        JAVA_TYPE_TO_SQL.put(Long.class, "BIGINT");
        JAVA_TYPE_TO_SQL.put(String.class, "VARCHAR(255)");
        JAVA_TYPE_TO_SQL.put(java.util.Date.class, "DATETIME");
        JAVA_TYPE_TO_SQL.put(Boolean.class, "TINYINT(1)");
        JAVA_TYPE_TO_SQL.put(java.math.BigDecimal.class, "DECIMAL(10,2)");
        JAVA_TYPE_TO_SQL.put(java.time.LocalDate.class, "DATE");
        JAVA_TYPE_TO_SQL.put(java.time.LocalDateTime.class, "DATETIME");
        JAVA_TYPE_TO_SQL.put(java.time.Instant.class, "TIMESTAMP");
        JAVA_TYPE_TO_SQL.put(byte[].class, "BLOB");
    }

    /**
     * 生成完整 SQL 脚本（包含表结构和外键约束）
     *
     * @param tables     表实体列表
     * @param allColumns 所有列实体列表
     * @return 可执行的 SQL 字符串
     */
    public static String generateFullSql(
            List<DatabaseTables> tables,
            List<TableColumns> allColumns
    ) {
        // 校验输入参数
        if (tables == null || tables.isEmpty()) {
            throw new IllegalArgumentException("表列表不能为空");
        }
        if (allColumns == null) {
            allColumns = Collections.emptyList();
        }

        // 构建表 ID 与表名的映射关系
        Map<String, Integer> tableNameToId = tables.stream()
                .collect(Collectors.toMap(
                        DatabaseTables::getTableName,
                        DatabaseTables::getTableId,
                        (existing, replacement) -> existing // 处理表名重复的情况
                ));
        Map<Integer, String> tableIdToName = tables.stream()
                .collect(Collectors.toMap(
                        DatabaseTables::getTableId,
                        DatabaseTables::getTableName
                ));

        // 按外键依赖排序表（确保被引用表优先创建）
        List<DatabaseTables> sortedTables = sortTablesByFkDependency(tables, allColumns);

        // 生成 CREATE TABLE 语句
        StringBuilder createSql = new StringBuilder();
        for (DatabaseTables table : sortedTables) {
            List<TableColumns> tableColumns = allColumns.stream()
                    .filter(col -> col.getTableId().equals(table.getTableId()))
                    .sorted(Comparator.comparingInt(TableColumns::getColumnPosition))
                    .collect(Collectors.toList());
            createSql.append(generateCreateTableSql(table, tableColumns)).append("\n\n");
        }

        // 生成外键约束语句
        String fkSql = generateFkConstraints(allColumns, tableIdToName);
        createSql.append(fkSql);

        return createSql.toString();
    }

    /**
     * 生成单表的 CREATE TABLE 语句
     */
    private static String generateCreateTableSql(DatabaseTables table, List<TableColumns> columns) {
        StringBuilder sql = new StringBuilder();
        sql.append("CREATE TABLE `").append(table.getTableName()).append("` (\n");

        boolean firstColumn = true;
        for (TableColumns column : columns) {
            if (!firstColumn) sql.append(",\n");

            // 生成列定义基础部分
            sql.append("  `").append(column.getColumnName()).append("` ")
                    .append(column.getDataType());

            // 添加约束条件
            if (column.getIsNullable() == 0) {
                sql.append(" NOT NULL");
            }

            // 处理特殊约束
            if (column.getDescription() != null) {
                String[] constraints = column.getDescription().split(" # ");
                for (String constraint : constraints) {
                    sql.append(" ").append(constraint
                            .replace(" (", "(") // 标准化括号格式
                            .replace("CHECK  ", "CHECK "));
                }
            }

            firstColumn = false;
        }

        // 处理主键
        List<TableColumns> primaryKeys = columns.stream()
                .filter(col -> col.getIsPrimaryKey() == 1)
                .toList();
        if (!primaryKeys.isEmpty()) {
            sql.append(",\n  PRIMARY KEY (")
                    .append(primaryKeys.stream()
                            .map(col -> "`" + col.getColumnName() + "`")
                            .collect(Collectors.joining(", ")))
                    .append(")");
        }

        sql.append("\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
        if (table.getDescription() != null && !table.getDescription().isEmpty()) {
            sql.append(" COMMENT='").append(escapeComment(table.getDescription())).append("'");
        }
        return sql.append(";").toString();
    }

    /**
     * 生成所有外键约束语句
     */
    private static String generateFkConstraints(
            List<TableColumns> allColumns,
            Map<Integer, String> tableIdToName
    ) {
        StringBuilder sql = new StringBuilder();

        // 按表分组处理外键列
        Map<Integer, List<TableColumns>> tableFkMap = allColumns.stream()
                .filter(col -> col.getIsForeignKey() == 1)
                .collect(Collectors.groupingBy(TableColumns::getTableId));

        for (Map.Entry<Integer, List<TableColumns>> entry : tableFkMap.entrySet()) {
            Integer sourceTableId = entry.getKey();
            String sourceTableName = tableIdToName.get(sourceTableId);
            if (sourceTableName == null) {
                continue; // 跳过无效的表ID
            }

            List<TableColumns> fkColumns = entry.getValue();
            for (TableColumns fkCol : fkColumns) {
                // 外键关联目标信息（校验非空）
                String targetTableName = fkCol.getForeignTableName();
                String targetColumnName = fkCol.getForeignColumnName();
                if (targetTableName == null || targetColumnName == null) {
                    continue; // 跳过信息不完整的外键
                }

                // 外键约束名（用下划线分隔，避免特殊字符）
                String constraintName = String.format(
                        "fk_%s_%s_%s",
                        sourceTableName,
                        targetTableName,
                        fkCol.getColumnName()
                ).replaceAll("[^a-zA-Z0-9_]", "_"); // 替换非法字符

                // 生成外键约束 SQL（表名和列名加反引号）
                sql.append("ALTER TABLE `").append(sourceTableName).append("`\n")
                        .append("  ADD CONSTRAINT `").append(constraintName).append("`\n")
                        .append("  FOREIGN KEY (`").append(fkCol.getColumnName()).append("`)\n")
                        .append("  REFERENCES `").append(targetTableName).append("` (`").append(targetColumnName).append("`)\n")
                        .append("  ").append(DEFAULT_CASCADE_STRATEGY).append(";\n");

                // 一对一关系添加唯一约束
                if (isUniqueColumn(allColumns, sourceTableId, fkCol.getColumnName())) {
                    sql.append("ALTER TABLE `").append(sourceTableName).append("`\n")
                            .append("  ADD UNIQUE KEY `uk_").append(sourceTableName).append("_").append(fkCol.getColumnName()).append("`\n")
                            .append("  (`").append(fkCol.getColumnName()).append("`);\n");
                }

                sql.append("\n");
            }
        }

        return sql.toString();
    }

    /**
     * 按外键依赖排序表（拓扑排序）
     */
    private static List<DatabaseTables> sortTablesByFkDependency(
            List<DatabaseTables> tables,
            List<TableColumns> allColumns
    ) {
        // 构建依赖图：key=表ID，value=依赖的表ID列表
        Map<Integer, Set<Integer>> dependencyGraph = new HashMap<>();
        tables.forEach(table -> dependencyGraph.put(table.getTableId(), new HashSet<>()));

        // 收集依赖关系
        allColumns.stream()
                .filter(col -> col.getIsForeignKey() == 1 && col.getForeignTableId() != null)
                .forEach(col -> {
                    Integer sourceId = col.getTableId();
                    Integer targetId = col.getForeignTableId();
                    if (dependencyGraph.containsKey(sourceId) && dependencyGraph.containsKey(targetId)) {
                        dependencyGraph.get(sourceId).add(targetId);
                    }
                });

        // 拓扑排序
        List<Integer> sortedTableIds = topologicalSort(dependencyGraph);

        // 转换为 DatabaseTables 列表（保持排序顺序）
        Map<Integer, DatabaseTables> idToTable = tables.stream()
                .collect(Collectors.toMap(DatabaseTables::getTableId, t -> t));
        return sortedTableIds.stream()
                .map(idToTable::get)
                .filter(Objects::nonNull) // 过滤无效的表
                .collect(Collectors.toList());
    }

    /**
     * 拓扑排序实现（处理表依赖）
     */
    private static List<Integer> topologicalSort(Map<Integer, Set<Integer>> graph) {
        List<Integer> result = new ArrayList<>();
        Set<Integer> visited = new HashSet<>();
        Set<Integer> temp = new HashSet<>();

        for (Integer node : graph.keySet()) {
            if (!visited.contains(node)) {
                dfs(node, graph, visited, temp, result);
            }
        }
        return result;
    }

    private static void dfs(
            Integer node,
            Map<Integer, Set<Integer>> graph,
            Set<Integer> visited,
            Set<Integer> temp,
            List<Integer> result
    ) {
        if (temp.contains(node)) {
            throw new IllegalArgumentException(
                    String.format("检测到循环依赖：表ID=%d 参与了循环引用", node)
            );
        }
        if (!visited.contains(node)) {
            temp.add(node);
            // 递归处理依赖的表（避免空指针）
            graph.getOrDefault(node, Collections.emptySet())
                    .forEach(dep -> dfs(dep, graph, visited, temp, result));
            temp.remove(node);
            visited.add(node);
            result.add(node);
        }
    }

    /**
     * 判断列是否有唯一约束（用于识别一对一关系）
     */
    private static boolean isUniqueColumn(
            List<TableColumns> allColumns,
            Integer tableId,
            String columnName
    ) {
        return allColumns.stream()
                .filter(col -> col.getTableId().equals(tableId) && columnName.equals(col.getColumnName()))
                .anyMatch(col -> "ONE_TO_ONE".equals(col.getForeignKeyDescription()));
    }

    /**
     * 解析 SQL 数据类型（优先使用 column 的 dataType，否则反向映射）
     */
    private static String resolveSqlType(TableColumns column) {
        // 优先使用已定义的 dataType
        if (column.getDataType() != null && !column.getDataType().trim().isEmpty()) {
            return column.getDataType().trim();
        }
        // 从 Java 类型反向映射
        for (Map.Entry<String, Class<?>> entry : TypeMapperUtils.TYPE_MAPPINGS.entrySet()) {
            if (entry.getValue().getCanonicalName().equals(column.getJavaType())) {
                return entry.getKey();
            }
        }
        // 默认类型
        return "VARCHAR(255)";
    }

    /**
     * 转义 SQL 注释中的单引号
     */
    private static String escapeComment(String comment) {
        return comment == null ? "" : comment.replace("'", "''");
    }
}